/*
 * Copyright (C) Francisco Morero Peyrona. All rights reserved.
 * 
 * This software is published under the terms of Open Source License version 1.1, 
 * a copy of which has been included with this distribution in the License.txt file.
 */

package jdbcmanager.tree;

import java.util.*;
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;

import jdbcmanager.dbtools.*;
import jdbcmanager.dbtools.datatypes.*;
import jdbcmanager.swing.*;

/**
 * Shows and handles the DBMS tree.
 * 
 * @author Francisco Morero Peyrona
 */
public final class DbTree extends JPanel implements ActionListener, DbChangeEventListener
{
	private Tree		tree;
	private TreeToolBar	toolbar;
	private JxPopupMenu	popup;
	private JActionSet	actions;
	
	private Vector vListeners;

	//-------------------------------------------------------------------------//

	/**
	 * Class constructor.
	 */
	public DbTree()
	{
		super( new BorderLayout() );

		this.vListeners = new Vector();
		
		init();

		//-------------------- Listeners ------------------------------//
		this.tree.addTreeSelectionListener( new TreeSelectionListener()
		{
			public void valueChanged( TreeSelectionEvent evt )
			{
				onTreeSelectionChange( evt );
			}
		} );
	}

	/**
	 * Refresh the tree and makes root node as the selected one.
	 */
	public void connectionStablished( String sConnTitle )
	{
		JdbcTreeNode root = (JdbcTreeNode) (this.tree.getModel().getRoot());

		if( sConnTitle == null || sConnTitle.trim().length() == 0 )
			sConnTitle = "???";

		root.setName( sConnTitle );
		this.tree.update( root );
		this.tree.setSelectionRow( 0 );         // Highlights root node
		root.setValidActions( this.actions );
		((JViewport) this.tree.getParent()).setViewPosition( new Point( 0,0 ) );   // Scrolls to top-left corner
		this.tree.repaint();
	}

	/**
	 * Processes all common actions from toolbar and popup.
	 * <p> 
	 * Note: ToolBar button 'DesktopTool' does not exist in popup menu
	 * 
	 * @param evt
	 */
	public void actionPerformed( ActionEvent evt )
	{
		AbstractButton item    = (AbstractButton) evt.getSource();
		String         sAction = item.getActionCommand();
		JdbcTreeNode   node    = (JdbcTreeNode) this.tree.getSelectionPath().getLastPathComponent();

		if( node == null )
			return;
		
		if( sAction.equals( "NEW" ) )
		{
			if( node.new_() )
			{
			    this.tree.update( node );
				fireDbChangeEvent( DbChangeEvent.TYPE_ENTITY_CREATED, node.ded, null );
			}
		}
		else if( sAction.equals( "EDIT" ) )
		{
			DbEntityDescriptor dedOld = (DbEntityDescriptor) node.ded.clone();
			
			if( node.edit() )
			{
				this.tree.update( node );
				fireDbChangeEvent( DbChangeEvent.TYPE_ENTITY_MODIFIED, node.ded, dedOld );
			}
		}
		else if( sAction.equals( "DELETE" ) )
		{
			if( node.delete() )
			{
			    this.tree.update( (JdbcTreeNode) node.getParent() );
				fireDbChangeEvent( DbChangeEvent.TYPE_ENTITY_REMOVED, node.ded, null );
			}
		}
		else if( sAction.equals( "RENAME" ) )
		{
			if( node.isEditable() )
				this.tree.startEditingAtPath( new TreePath( node.getPath() ) );
		}
		else if( sAction.equals( "OPEN"    ) )	this.tree.executeAction( node );
		else if( sAction.equals( "REFRESH" ) )	this.tree.update( node );
		else if( sAction.equals( "INFO"    ) )  node.info();
	}

	//-------------------------------------------------------------------------//
	// Other functions: events
	//-------------------------------------------------------------------------//

	public void addDbChangeListener( DbChangeEventListener listener )
	{
		if( ! this.vListeners.contains( listener ) )
			this.vListeners.add( listener );
	}

	public void removeDbChangeListener( DbChangeEventListener listener )
	{
		this.vListeners.remove( listener );
	}

	private void fireDbChangeEvent( int nType, DbEntityDescriptor ded, DbEntityDescriptor dedOld )
	{
		DbChangeEvent event = new DbChangeEvent( this, nType, ded );
		
		if( dedOld != null )
			event.setOldDbEntityDescriptor( dedOld );

		for( Enumeration e = this.vListeners.elements(); e.hasMoreElements(); )
			((DbChangeEventListener) e.nextElement()).handleDbChangeEvent( event );
	}
	
	/**
	 * @see jdbcmanager.dbtools.DbChangeEventListener#handleDbChangeEvent(jdbcmanager.diagram.DiagramEvent)
	 */ 
	public void handleDbChangeEvent( DbChangeEvent event )
	{
		DbEntityDescriptor dedNew = event.getDbEntityDescriptor();
		DbEntityDescriptor dedOld = event.getOldDbEntityDescriptor();
		JdbcTreeNode       node   = null;
		
		if( event.getID() == DbChangeEvent.TYPE_ENTITY_MODIFIED )
		{
			node = this.tree.getNode( dedNew );
			
			// Something was modifed but it is not the title, then it was the structure: not interesting
			if( node instanceof NodeTable && dedNew.getName().equals( dedOld.getName() ) )
				node = null;
		}
		else if( event.getID() == DbChangeEvent.TYPE_ENTITY_REMOVED )
		{
			node  = this.tree.getNode( dedOld );
		}
		else if( event.getID() == DbChangeEvent.TYPE_ENTITY_CREATED )
		{
			node = this.tree.getNode( new DbEntityDescriptor( dedNew.getCatalog(), dedNew.getSchema(), null ) );
		}
		
		if( node != null )
			this.tree.update( node );
	}

	//-------------------------------------------------------------------------//

	/**
	 * Invoked by 'TreeSelectionListener'. 
	 * It is called every time selected node changes: used to update the 
	 * "enable status" of toolbar and popup menu.
	 */
	private void onTreeSelectionChange( TreeSelectionEvent evt )
	{
		TreePath path = evt.getNewLeadSelectionPath();

		if( path != null )
		{
			JdbcTreeNode node = (JdbcTreeNode) path.getLastPathComponent();

			if( node != null )
				node.setValidActions( this.actions );
		}
		else		// No node is selected -> Disable all buttons except Desktool button (last one)
		{
			this.toolbar.disableAllExceptDesktop();
		}
	}

	private void init()
	{
		this.actions = new JActionSet();
		this.actions.addActionListener( this );

		this.actions.add( new ImageIcon( getClass().getResource( "images/tb_new.gif" ) ), 
				          "New", "Add new element (on root node open connection dialog)", "NEW" );

		this.actions.add( new ImageIcon( getClass().getResource( "images/tb_open.gif" ) ), 
				          "Open", "Open highlighted element in an internal window", "OPEN" );

		this.actions.add( new ImageIcon( getClass().getResource( "images/tb_edit.gif" ) ), 
				          "Edit", "Edit highlighted element", "EDIT" );

		this.actions.add( new ImageIcon( getClass().getResource( "images/tb_rename.gif" ) ), 
		                  "Rename", "Rename highlighted element (also with click)", "RENAME" );
		
		this.actions.add( new ImageIcon( getClass().getResource( "images/tb_delete.gif" ) ), 
				          "Delete", "Delete permanently highlighted element", "DELETE" );

		this.actions.add( new ImageIcon( getClass().getResource( "images/tb_refresh.gif" ) ), 
				          "Refresh", "Reload tree for selected node and childs", "REFRESH" );

		this.actions.add( new ImageIcon( getClass().getResource( "images/tb_info.gif" ) ), 
				          "Information", "Provide information about highlighted element (even about root node)", "INFO" );

		this.tree    = new Tree();
		this.toolbar = new TreeToolBar( this.actions );
		this.popup   = new JxPopupMenu( this.actions );

		this.tree.setPopupMenu( this.popup );
		this.toolbar.setEnabled( false );
		
		add( new JScrollPane( this.tree ), BorderLayout.CENTER );
		add( this.toolbar, BorderLayout.NORTH );
	}
}